package chromo;

import java.util.ArrayList;

import fitness.FitnessCalc;
import gene.ChinkleException;
import gene.Gene;
import gene.Input;

public class Chromosome {
	public static int size = 2;
	public static int defaultInputs = 3;
	private int fitness = 0;
	private Gene[][] genes = new Gene[size][size];
	private ArrayList<Input> inputs;
	private ArrayList<ArrayList<Boolean>> outputs;
	
	// Takes the default inputs and initializes all the genes
	public Chromosome(ArrayList<Input> in) throws ChinkleException{
		inputs = new ArrayList<Input>(in);
		for(int i = 0; i < genes.length; i++){
			for(int j = 0; j < genes[i].length; j++){
				genes[i][j] = new Gene(i,j,inputs,genes);
			}
		}
	}
	
	// Takes an old chromosome and makes a copy
	public Chromosome(Chromosome in){
		inputs = new ArrayList<Input>(in.inputs);
		fitness = 0;
		genes = (Gene[][])in.genes.clone();
		outputs = null;
	}
	public String printGenes(){
		String s = "";
	
		for(Gene[] geneArray : genes){
			for(Gene gene : geneArray){
				s += gene+ "\n";
			}
		}
		return s;
	}
	
	//Will actually go through all possibilities for the system and calculate outputs
	public void solve(){
		if(outputs == null){
			outputs = new ArrayList<ArrayList<Boolean>>();
			boolean[][] tt = convertTT();
			for(boolean[] truthTable : tt){
				setInputs(truthTable);
				solveGenes();

				
			}
		}
		
	}
	//Simple setter for genes
	public void setGene(Gene in, int posDown, int posRight){
		genes[posDown][posRight] = in;
	}
	//Getter for genes
	public Gene getGene(int posDown, int posRight){
		return genes[posDown][posRight];
	}
	//Actually returns a 2d arraylist of the outputs
	public ArrayList<ArrayList<Boolean>> getAllOutputs(){
		solve();
		return getOutputs();
	}
	//Will solve all the genes
	public void solveGenes(){
		for(Gene[] geneArray : genes){
			for(Gene gene : geneArray){
				gene.solve();
			}
		}
		
	}
	
	//Sets the initial inputs
	public void setInputs(boolean[] boolInputs){
		int inputNum = 2;
		for(boolean input : boolInputs){
			inputs.get(inputNum).setBool(input);
			inputNum++;
		}
	}
	//Calculates which of the bottom and right edge outputs and stores them
	public ArrayList<ArrayList<Boolean>> getOutputs(){
		outputs.clear();
		for(int i = 0; i < size; i++){
			if(size-1 == i){

				outputs.add(genes[i][i].outputDownValues);
				outputs.add(genes[i][i].outputRightValues);
			}
			else{
				//TODO MAKE SURE THESE ARE GRABBED FROM THE RIGHT PLACE
				outputs.add(genes[size-1][i].outputDownValues);
				outputs.add(genes[i][size-1].outputRightValues);
			}
		}
		return outputs;
	}
	//Print statement
	public String toString(){
		String out = "";
		for(Gene[] geneArray : genes)
			for(Gene gene : geneArray)
				out += gene + "\n\n";
		return out;
		
	}
	
	/**
	 * Gets the Truth Table.  Generates a 2d int array of 1's and 0's for input
	 * truths.
	 *
	 * @return the tt
	 */
	public int[][] getTT(){
		int intInput = Chromosome.defaultInputs;
		int rowsArray = (int)Math.pow(2, intInput);
		int[][] output = new int[rowsArray][intInput];
		for(int i = 0; i<rowsArray; i++){
			for(int j = intInput-1; j>=0; j--){
				output[i][intInput-1-j] = (i / ((int) Math.pow(2, j))) % 2;
			}
		}

		return output;
	}
	
	/**
	 * Converts the above Truth Table to a 2d boolean value of true's and false's
	 * instead of 1's and 0's.
	 *
	 * @return the boolean[][]
	 */
	public boolean[][] convertTT(){
		int[][] input = getTT();
		boolean[][] output = new boolean[input.length][input[0].length];
		for(int i = 0; i < input.length; i++){
			for(int j = 0; j < input[i].length; j++){
				if(input[i][j] == 1)
					output[i][j] = true;
				else
					output[i][j] = false;					
				
			}
		}
		return output;
	}
	public void setFitness(int fit){
		fitness = fit;
	}
	public int getFitness(){
		if (fitness == 0) {
        	fitness = FitnessCalc.getFitness(this);
        }
        return fitness;
	}
	public void mutate(int i, int j) throws ChinkleException {
		outputs = null;
		genes[i][j].mutate();
		
	}
}
